// Copyright (c) 2021 Presto Labs Pte. Ltd.
// Author: daniel

#pragma once

#include <list>
#include <string>
#include <utility>
#include <vector>

#include "coin2/exchange/base/api_base/asio_http_context.h"
#include "coin2/exchange/base/api_base/base_auth.h"
#include "coin2/exchange/base/api_util/auth_key.h"
#include "coin2/exchange/base/api_util/url_params_builder.h"

class LmaxdigitalBaseAuth {
 public:
  using HeaderSet = std::vector<std::pair<std::string, std::string>>;

 public:
  LmaxdigitalBaseAuth() {
    venue_ = "DIGITAL";
    api_key_ = "ec18a731509b47e89d3ccf89b777f902";
  }
  virtual ~LmaxdigitalBaseAuth() = default;

  inline void SetApiKey(std::optional<std::string> api_key) {
    if (api_key) {
      api_key_ = *api_key;
    }
    CHECK_THROW(!api_key_.empty()) << "[lmax_digital] api_key is empty";
  }
  inline void SetVenue(std::optional<std::string> venue) {
    if (venue) {
      venue_ = *venue;
    }
    CHECK_THROW(!venue_.empty()) << "[lmax_digital] venue is empty";
  }
  inline void SetLoginCount(int login_count) { login_count_ = login_count; }
  inline void SetUsername(std::optional<std::string> username) {
    if (username) {
      username_ = *username;
    }
    CHECK_THROW(!username_.empty()) << "[lmax_digital] username is empty";
  }
  inline void SetPassword(std::optional<std::string> password) {
    if (password) {
      password_ = *password;
    }
    CHECK_THROW(!password_.empty()) << "[lmax_digital] password is empty";
  }
  inline void SetAccessKey(std::optional<std::string> access_key) {
    if (access_key) {
      access_key_ = *access_key;
    }
    CHECK_THROW(!access_key_.empty()) << "[lmax_digital] rest_access_key is empty";
  }
  inline void SetSecretKey(std::optional<std::string> secret_key) {
    if (secret_key) {
      secret_key_ = *secret_key;
    }
    CHECK_THROW(!secret_key_.empty()) << "[lmax_digital] rest_secret_key is empty";
  }
  inline void SetToken(const std::string& token) { token_ = token; }
  inline auto GetToken() const -> std::string { return token_; }
  auto GenBodyJsonString() const -> std::string;
  auto GenSignature(
      const std::string& access_key,
      const std::string& nonce_str,
      const std::string& ts_str,
      const std::string& secret_key) const -> std::string;

 private:
  std::string venue_;
  std::string username_;
  std::string password_;
  std::string api_key_;
  int login_count_ = 0;
  std::string access_key_;
  std::string secret_key_;
  std::string token_;
};

class LmaxdigitalRestAuth : public LmaxdigitalBaseAuth, public BaseRestAuth {
 public:
  explicit LmaxdigitalRestAuth(
      const std::string& key_filepath,
      const std::string& host,
      const std::string& port,
      const std::string& bind_ip)
      : key_(AuthKey::FromFile(key_filepath)), host_(host), port_(port), bind_ip_(bind_ip) {
    SetVenue(key_.GetValue("venue"));
    SetUsername(key_.GetValue("rest_username"));
    SetPassword(key_.GetValue("rest_password"));
    SetApiKey(key_.GetValue("rest_api_key"));
    SetAccessKey(key_.GetValue("rest_access_key"));
    SetSecretKey(key_.GetValue("rest_secret_key"));
  }
  explicit LmaxdigitalRestAuth(
      const std::string& key_filepath,
      const std::string& host,
      int32_t port,
      const std::string& bind_ip)
      : key_(AuthKey::FromFile(key_filepath)),
        host_(host),
        port_(std::to_string(port)),
        bind_ip_(bind_ip) {
    SetVenue(key_.GetValue("venue"));
    SetUsername(key_.GetValue("rest_username"));
    SetPassword(key_.GetValue("rest_password"));
    SetApiKey(key_.GetValue("rest_api_key"));
    SetAccessKey(key_.GetValue("rest_access_key"));
    SetSecretKey(key_.GetValue("rest_secret_key"));
  }

  AsioHttpContext* CreateRequestByGetWithSignature(
      const std::string& address,
      UrlParamsBuilder* builder) override;
  AsioHttpContext* CreateRequestByPostWithSignature(
      const std::string& address,
      UrlParamsBuilder* builder) override;
  AsioHttpContext* CreateRequestByDeleteWithSignature(
      const std::string& address,
      UrlParamsBuilder* builder) override {
    return nullptr;
  }
  AsioHttpContext* CreateRequestByPutWithSignature(
      const std::string& address,
      UrlParamsBuilder* builder) override {
    return nullptr;
  }

 protected:
  AuthKey key_;
  std::string host_;
  std::string port_;
  std::string bind_ip_;
};
